package com.as.db.dao;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.Example;
import org.springframework.orm.hibernate3.HibernateTemplate;

import com.as.db.HibernateSessionFactory;
import com.as.db.exception.CreateException;
import com.as.db.exception.DeleteException;
import com.as.db.exception.FinderException;
import com.as.db.exception.UpdateException;

public class GenericDAO<T> implements IGenericDAO<T> {

	//private Session session;
	protected HibernateTemplate hibernateTemplate;
	private Class<T> miClaseT;
	
	protected GenericDAO() {
		this.miClaseT = getMiClaseT();
	}
	
	public <T> GenericDAO(Class miclase) {
		if (miclase == null)
			miClaseT = getMiClaseT();
		else 
			miClaseT = miclase;
	}
	
	protected Class<T> getMiClaseT(){
		if (miClaseT == null) {
			Class<?> clase = getClass();
			Type tipo = clase.getGenericSuperclass();
			ParameterizedType paramType = (ParameterizedType)tipo;
			Type arrayType[] = paramType.getActualTypeArguments();
			miClaseT = (Class<T>)arrayType[0];
		}
		return miClaseT;
	}

	/**
	 * @return una instancia T(object del usuario)
	 * 
	 * @param id
	 */
	public T load(Long id){
		return this.hibernateTemplate.load(miClaseT, id);
	}

	/**
	 * 
	 * @param entity
	 * @exception UpdateException
	 */
	public void update(T entity) {
	  	this.hibernateTemplate.update(entity);
	}

	/**
	 * 
	 * @param entity
	 * @exception CreateException
	 */
	public void save(T entity) {
		this.hibernateTemplate.save(entity);
	}

	/**
	 * 
	 * @param entity
	 * @exception DeleteException
	 */
	public void delete(T entity) {
		this.hibernateTemplate.delete(entity);
	}

	/**
	 * 
	 * @exception FinderException
	 */
	public List<T> getList() {
		return this.hibernateTemplate.findByNamedQuery("from " + miClaseT.getName());
	}

	/**
	 * 
	 * @param id
	 * @exception DeleteException
	 */
	public void deleteById(Long id) {
		T entity = load(id);
		delete(entity);
	}

	/**
	 * 
	 * @param isSure
	 * @exception DeleteException
	 */
	public int deleteAll() {
		Session session = this.hibernateTemplate.getSessionFactory().getCurrentSession();
		session.beginTransaction();
		int countDeleted = session.createQuery("delete " + miClaseT.getName()).executeUpdate();
		session.getTransaction().commit();
		return countDeleted;
	}

	public int count(){
		Session session = this.hibernateTemplate.getSessionFactory().getCurrentSession();
		session.beginTransaction();
		Integer count = (Integer) session.createQuery("select count(*) from " + miClaseT).uniqueResult();
		session.getTransaction().commit();
		return count.intValue();
	}

	/**
	 * 
	 * @param entity
	 */
	public List<T> findByExample(T entityExample){
		Example example = Example.create(entityExample).excludeZeroes().enableLike().ignoreCase();
		return this.hibernateTemplate.findByExample(entityExample);
	}

	public List<T> getList(String queryName) {
		return this.hibernateTemplate.findByNamedQuery(queryName);
	}
	
	public List<T> getList(String queryName, Object param) {
		Session session = this.hibernateTemplate.getSessionFactory().getCurrentSession();
		Query query = session.getNamedQuery(queryName);
		setParameterToQuery(query,param);
		List<T> entityList = query.list();
		session.getTransaction().commit();
		return entityList;
	}
	
	public void setParameterToQuery(Query query, Object param) {
		if (param instanceof Long) {
			query.setParameter(0, param);
		} else if (param instanceof String) {
			query.setParameter(0, param);
		} else  if (param instanceof Date) {
			query.setParameter(0, param);
		} else if (param instanceof List) {
			int i = 0;
			for (Object o : (List)param) {
				query.setParameter(i, o); 
			}
		} else if (param instanceof Map) {
			Map m = (Map)param;
			for(Object o : m.keySet()) {
				String k = (String)o;
				Object v = m.get(k);
				if (v instanceof Collection) {
					query.setParameterList(k, (Collection)v);
				} else {
					query.setParameter(k, v);
				}
			}
		}
	}

	protected HibernateTemplate getHibernateTemplate() {
		return hibernateTemplate;
	}

	public void setHibernateTemplate(HibernateTemplate hibernateTemplate) {
		this.hibernateTemplate = hibernateTemplate;
	}
}